package com.smsc.headend.task.engine.handler.impl.firmware;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.enums.DeviceType;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.common.enums.errorcode.TaskHandlerErrorCode;
import com.smsc.headend.module.asset.entity.ComEntity;
import com.smsc.headend.module.fwu.entity.LogFwUpgradeHis;
import com.smsc.headend.module.fwu.entity.TemplateFwUpgrade;
import com.smsc.headend.module.protocol.DeviceData;
import com.smsc.headend.module.protocol.cosem.bo.CosemData;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskHandlerException;
import com.smsc.headend.task.engine.handler.TaskHandler;
import com.smsc.headend.task.engine.service.CosemXmlService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.firmware.FirmwareService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.util.stream.Stream;

/**
 * 获取TransferStatus的三种情景
 * TaskType {@link com.smsc.headend.module.task.enums.TaskType#FirmwareImageTransfer}
 * 1. Init方法之后，开始传输固件文件之前
 * 2. 固件文件完整 进行ImageVerify返回Temporary failure之后
 * TaskType {@link com.smsc.headend.module.task.enums.TaskType#FirmwareImageActivation}
 * 3. 固件激活后返回Temporary failure  , loop直到返回Image activation successful
 *
 * @author liangli
 * @date 2020/9/25
 */
@Slf4j
public class ReadFirmwareTransferredStatusHandler implements TaskHandler {

    public static final int INIT_STATUS_READ = 1;
    public static final int VERIFY_STATUS_READ = 2;
    public static final int ACTIVATION_STATUS_READ = 3;


    @Autowired
    CosemXmlService cosemXmlService;
    @Autowired
    TaskDAO taskDAO;
    @Value("#{T(java.lang.Integer).parseInt('${fwu.step.retryTimes:2}')}")
    Integer firmwareStepsRetry;
    @Autowired
    FirmwareService firmwareService;

    @Override
    public int handle(Task task, AtomicTask atomicTask, DeviceData deviceData, String actionResult) throws UDISTaskHandlerException {
        if (!(deviceData instanceof CosemData)) {
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.DEVICE_DATA_TYPE_NOT_SUPPORT.toString());
        }

        CosemData cosemData = (CosemData) deviceData;
        Integer transferStatus = Convert.toInt(cosemXmlService.getData(cosemData.getType(), cosemData.getValue()));
        if (transferStatus == null) {
            log.error("get transfer status failed,taskNo={}, comId={}, meterId={}, data={}", task.getTaskNo(), task.getComId(), task.getMeterId(), JSONUtil.toJsonStr(cosemData));
            return -1;
        }
        ImageTransferStatus status = ImageTransferStatus.forValue(transferStatus);
        Integer seq = atomicTask.getSeq();
        Integer situation = getReadStatusSituation(task, atomicTask);
        log.info("{}: get transfer status, meterId={}, comId={}, status={}, taskType={}, situation={}, seq={}, param={}", task.getTaskType(), task.getMeterId(), task.getComId(), status.toString(), task.getTaskType(), situation, seq, JSONUtil.toJsonStr(getParameterMap(task, atomicTask)));
        if (situation == INIT_STATUS_READ) {
            return initStatusHandle(status);
        }

        if (situation == VERIFY_STATUS_READ) {
            return verifyStatusHandle(status, task, atomicTask);
        }

        if (situation == ACTIVATION_STATUS_READ) {
            return activationStatusHandle(status, task, atomicTask);
        }
        return -1;
    }

    private int activationStatusHandle(ImageTransferStatus status, Task task, AtomicTask atomicTask) {
        switch (status) {
            case activationInitiated:
            case activationSuccess:
                saveFwuReport(task, atomicTask, 1);
                return 1;
            case activationFailed:
            case TransferNotInit:
            case TransferInitiated:
            case VerificationInitiated:
            case VerificationSuccess:
            case VerificationFail:
            default:
                return -1;
        }
    }

    private int verifyStatusHandle(ImageTransferStatus status, Task task, AtomicTask atomicTask) {
        switch (status) {
            case VerificationInitiated:
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    return -1;
                }
                return 0;
            case VerificationSuccess:
                saveFwuReport(task, atomicTask, 1);
                return 1;
            case VerificationFail:
                saveFwuReport(task, atomicTask, -1);
                return -1;
            case TransferNotInit:
            case TransferInitiated:
            case activationInitiated:
            case activationSuccess:
            case activationFailed:
            default:
                return -1;
        }

    }

    private int initStatusHandle(ImageTransferStatus status) {
        switch (status) {
            case TransferNotInit:
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    return -1;
                }
                return 0;
            case TransferInitiated:
                return 1;
            case VerificationInitiated:
            case VerificationSuccess:
            case VerificationFail:
            case activationInitiated:
            case activationSuccess:
            case activationFailed:
            default:
                return -1;
        }
    }

    private Integer getReadStatusSituation(Task task, AtomicTask atomicTask) {
        if (TaskType.FirmwareImageTransfer.toString().equalsIgnoreCase(task.getTaskType())) {
            if (atomicTask.getSeq() < 5) {
                return INIT_STATUS_READ;
            }

            return VERIFY_STATUS_READ;
        }
        return ACTIVATION_STATUS_READ;
    }

    private void saveFwuReport(Task task, AtomicTask atomicTask, Integer result) {
        TemplateFwUpgrade.TaskType taskType = TaskType.FirmwareImageTransfer.toString().equalsIgnoreCase(task.getTaskType()) ? TemplateFwUpgrade.TaskType.ImageTransfer
                : TemplateFwUpgrade.TaskType.ImageActivation;
        firmwareService.saveFwuReport(task, atomicTask, result, taskType, null, null);
    }

    @Override
    public int handleFailure(Task task, AtomicTask atomicTask, String message) {
        if (firmwareService.isRetryMessageType(message)) {
            AtomicTaskState atomicTaskState = taskDAO.hashGetAtomicTaskState(atomicTask.getAtomicTaskNo());
            if (atomicTaskState.getRetryTimes() >= firmwareStepsRetry) {
                log.error("Iget image status failed retry times over");
                saveFwuReport(task, atomicTask, -1);
                return -1;
            }
            log.error("Image transfer failed retry times {}", atomicTaskState.getRetryTimes());
            atomicTaskState.setStored(false);
            taskDAO.hashSetAtomicTaskState(atomicTask.getAtomicTaskNo(), task, atomicTaskState.getRetryTimes() + 1,  atomicTaskState);
            return 0;
        }
        log.error("get image status failed, comId={},meterId={}， message={}", task.getComId(), task.getMeterId(), message);
        saveFwuReport(task, atomicTask, -1);
        return -1;
    }

    public enum ImageTransferStatus {
        TransferNotInit(0), TransferInitiated(1), VerificationInitiated(2), VerificationSuccess(3), VerificationFail(4), activationInitiated(5), activationSuccess(6), activationFailed(7);
        Integer code;

        ImageTransferStatus(int i) {
            this.code = i;
        }

        public static ImageTransferStatus forValue(Integer transferStatus) {
            return Stream.of(ImageTransferStatus.values()).filter(imageTransferStatus -> imageTransferStatus.getCode().intValue() == transferStatus).findAny().orElse(null);
        }

        public Integer getCode() {
            return code;
        }
    }
}
